﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeRepositoryConsole.Concurrency
{
    /// <summary>
    /// 
    /// </summary>
    public class LockObjectsStatic
    {
        /// <summary>
        /// Get same lock object for same key, prevent lock on String.Intern
        /// </summary>
        /// <param name="key"></param>
        /// <seealso cref="http://stackoverflow.com/questions/6983714/locking-on-an-interned-string"/>
        /// <returns></returns>
        public static object GetLockObject(string key)
        {
            // The value returned from GetHashCode can be positive or negative, 
            // but we can't index into the _stripes array using a negative index. 
            // And-ing with 0x7FFFFFFF will ensure that the index is always positive.
            int stripeIndex = (key.GetHashCode() & 0x7FFFFFFF) % _stripes.Length;
            return _stripes[stripeIndex];
        }

        private static readonly object[] _stripes = Enumerable.Range(0, 32)
            .Select(x => new object())
            .ToArray();
    }


    /// <summary>
    /// <seealso cref="http://blog.axantum.com/2008/01/lock-object-sharing-with-hashes.html"/>
    /// </summary>
    public class LockObjects
    {
        private object[] _lockObjects;

        public LockObjects(int numberOfLockObjects)
        {
            _lockObjects = GetInitialLockObjects(numberOfLockObjects);
        }

        public LockObjects()
            : this(20)
        {
        }

        private object[] GetInitialLockObjects(int numberOfLockObjects)
        {
            object[] lockObjects = new object[numberOfLockObjects];
            for (int i = 0; i < lockObjects.Length; ++i)
            {
                lockObjects[i] = new object();
            }
            return lockObjects;
        }

        public virtual object GetLockObject(params string[] keys)
        {
            int lockHash = 0;
            foreach (string key in keys)
            {
                lockHash += key.ToLowerInvariant().GetHashCode();
            }
            lockHash = Math.Abs(lockHash) % _lockObjects.Length;

            return _lockObjects[lockHash];
        }
    }

}
